package com.jiazhong.boot.redis.shuxin.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiazhong.boot.commons.bean.plus.Emp;
import com.jiazhong.boot.commons.util.JsonResult;
import com.jiazhong.boot.commons.util.ResultTool;
import com.jiazhong.boot.redis.shuxin.mapepr.EmpMapper;
import com.jiazhong.boot.redis.shuxin.service.EmpService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements EmpService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public JsonResult findById1(int id) {
        ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
        // 1. 首先在redis中查找
        String userStr = forValue.get("user:" + id);
        // 1.1 如果存在，返回redis中的信息
        if (userStr != null) {
            Emp emp = JSONArray.parseObject(userStr, Emp.class);
            return ResultTool.success(emp);
        }
        // 1.2 如果不存在，查询数据库
        // 2. 查询数据库
        Emp emp = getById(id);
        // 3. 如果数据库中没有该数据，返回null
        if (emp == null) {
            return ResultTool.fail(501, "没有对应id的数据");
        }
        // 4. 存放到redis中
        forValue.set("user:" + id, JSONArray.toJSONString(emp));
        // 5. 返回数据
        return ResultTool.success(emp);
    }

    @Override
    public JsonResult deleteById1(int id) {
        // 1. 删除数据库中对应的员工信息
        boolean b = removeById(id);
        if (!b) {
            return ResultTool.fail(501, "删除员工失败");
        }
        // 2. 删除redis中的该员工信息
        Boolean delete = stringRedisTemplate.delete("user:" + id);
        log.info("delete:{}", delete);
        return ResultTool.success(delete);
    }

    @Override
    public JsonResult updateEmp1(Emp emp) {
        // 1. 修改员工信息
        boolean b = updateById(emp);
        if (!b) {
            return ResultTool.fail(501, "修改员工失败");
        }
        // 2. 查询redis中是否存在该数据
        ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
        String s = forValue.get("user:" + emp.getEmpNo());
        if (s == null) {
            // 3. redis中没有该数据
            return ResultTool.success("success");
        }
        // 4. 如果存在这个数据，覆盖
        forValue.set("user:" + emp.getEmpNo(), JSONArray.toJSONString(emp));
        return ResultTool.success("success");
    }


    /*
        注解在⽅法上，表示该⽅法的返回结果是可以缓存的。也就是说，该⽅法的返回结果会放在缓存中，
        以便于以后使⽤相同的参数调⽤该⽅法时，会返回缓存中的值，⽽不会实际执⾏该⽅法。
    */
    // @Cacheable(cacheNames = "user", key = "#id", condition = "#id>8050")
    @Cacheable(cacheNames = "user", key = "#id")
    public JsonResult findById2(int id) {
        log.info("开始查询数据库，检索：{}的数据", id);
        return ResultTool.success(getById(id));
    }

    // 清空所有的缓存数据
    // @CacheEvict(allEntries = true)
    @CacheEvict(cacheNames = "user", key = "#id")
    public JsonResult deleteById2(int id) {
        return ResultTool.success(removeById(id));
    }

    /*
    作⽤主要针对⽅法配置，能够根据⽅法的请求参数对其结果进⾏缓存，和 @Cacheable 不同的是，它每次都会触发真实⽅法的调⽤。
    ⼀般⽤于更新操作，调⽤这个注解后会访问数据库，并将数据库的内容同步到Redis 中。
     */
    @CachePut(cacheNames = "user", key = "#emp.empNo")
    public JsonResult updateEmp2(Emp emp) {
        log.info("开始更新数据库和redis中的数据");
        updateById(emp);
        return ResultTool.success(getById(emp.getEmpNo()));
    }


    @Override
    public JsonResult findPage1(int page) {
        log.info("开始查找第{}页数据", page);
        ListOperations<String, String> forList = stringRedisTemplate.opsForList();
        Long size = forList.size("LIST");
        log.info("redis中存放的条数是：{}", size);
        if (size == null || size == 0 || size <= (page - 1) * 10) {
            log.info("该页数据在redis中不存在，查询数据库");
            IPage<Emp> iPage = Page.of(page, 10);
            List<Emp> list = list(iPage);
            // forList.rightPush()
            for (Emp emp : list) {
                forList.rightPush("LIST", JSONArray.toJSONString(emp));
            }
            iPage.setRecords(list);
            return ResultTool.success(iPage);
        }
        log.info("redis中存在该页数据");
        List<String> list = forList.range("LIST", (page - 1) * 10, page * 10);

        IPage<Emp> iPage = Page.of(page, 10);
        List<Emp> emps = new ArrayList<>();
        for (String s : list) {
            emps.add(JSONArray.parseObject(s, Emp.class));
        }
        iPage.setRecords(emps);
        return ResultTool.success(iPage);
    }

    @Cacheable(cacheNames = "emp_page", key = "#page", condition = "#page>0")
    public JsonResult findPage2(int page) {
        IPage<Emp> iPage = Page.of(page, 10);
        List<Emp> list = list(iPage);
        iPage.setRecords(list);
        return ResultTool.success(iPage);
    }

    // mysql和redis的数据同步问题
    public JsonResult findPage3(int page) {
        log.info("开始查询第{}页数据", page);
        ListOperations<String, String> forList = stringRedisTemplate.opsForList();
        int start = (page - 1) * 10;
        int end = start + 9;
        // 1. 判断是否是第一次查询 如果是第一次查询需要填充null
        Long size = forList.size("emp_page");
        log.info("redis_size:{}", size);
        if (size == null || size == 0) {
            log.info("第一次查询，不存在redis中的数据，填充");
            // 2.1 该页数据在redis中不存在，查询数据库
            IPage<Emp> iPage = Page.of(page, 10);
            List<Emp> sqlList = list(iPage);
            // 2.2 给redis中设置N个null来填充内容
            for (int i = 0; i < iPage.getTotal(); i++) {
                forList.rightPush("emp_page", "null");
            }
            // 2.3 将数据库查询出来的数据存放到redis指定的页中
            int index = start;
            for (Emp emp : sqlList) {
                // forList.rightPush("emp_page", JSONArray.toJSONString(emp));
                forList.set("emp_page", index, JSONArray.toJSONString(emp));
                index++;
            }
            iPage.setRecords(sqlList);
            return ResultTool.success(iPage);
        }
        log.info("不是第一次查询，检索该页数据是否存在");
        // 3. 判断该页在redis中是否存在
        List<String> list = forList.range("emp_page", start, end);
        log.info("redis_list:{}", list);
        if (list != null && list.get(0).equals("null")) {
            log.info("该页数据不存在，查询数据库");
            IPage<Emp> iPage = Page.of(page, 10);
            List<Emp> sqlList = list(iPage);
            int index = start;
            for (Emp emp : sqlList) {
                forList.set("emp_page", index, JSONArray.toJSONString(emp));
                index++;
            }
            iPage.setRecords(sqlList);
            return ResultTool.success(iPage);
        }
        log.info("该页数据存在，返回数据");
        // 3. redis中存在该页数据 将redis中的string集合转换成emp集合
        List<Emp> emps = new ArrayList<>();
        for (String s : list) {
            emps.add(JSONArray.parseObject(s, Emp.class));
        }
        IPage<Emp> iPage = Page.of(page, 10);
        iPage.setRecords(emps);
        return ResultTool.success(iPage);
    }

    @Override
    public JsonResult updateEmp3(Emp emp) {
        boolean b = updateById(emp);
        if (!b) {
            return ResultTool.fail(501, "修改员工失败");
        }
        // 删除redis中的数据
        ListOperations<String, String> forList = stringRedisTemplate.opsForList();
        List<String> all = forList.range("emp_page", 0, -1);
        AtomicInteger index = new AtomicInteger();
        if (all != null) {
            all.forEach(e -> {
                if (!"null".equals(e)) {
                    log.info("e:{}", e);
                    Emp emp1 = JSONArray.parseObject(e, Emp.class);
                    log.info("emp:{}", emp);
                    if (Objects.equals(emp.getEmpNo(), emp1.getEmpNo())) {
                        forList.set("emp_page", index.get(), JSONArray.toJSONString(emp));
                    }
                    index.getAndIncrement();
                }

            });
        }
        return ResultTool.success("success");
    }

    @Override
    public JsonResult deleteById3(int id) {
        boolean b = removeById(id);
        if (!b) {
            return ResultTool.fail(501, "删除失败");
        }
        // 删除redis中的数据
        ListOperations<String, String> forList = stringRedisTemplate.opsForList();
        List<String> all = forList.range("emp_page", 0, -1);
        if (all != null) {
            all.forEach(e -> {
                if (!"null".equals(e)) {
                    log.info("e:{}", e);
                    Emp emp = JSONArray.parseObject(e, Emp.class);
                    log.info("emp:{}", emp);
                    if (id == emp.getEmpNo()) {
                        forList.remove("emp_page", 1, e);
                    }
                }

            });
        }
        return ResultTool.success("success");
    }
}
